package com.lgl.scs.wx.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.lgl.scs.base.controller.BaseController;
import com.lgl.scs.util.service.UtilService;
import com.lgl.scs.util.utils.ControllerUtil;
import com.lgl.scs.wx.service.WxUserService;
import com.lgl.scsapi.system.model.AppUser;
import com.lgl.scsapi.system.model.PageData;
import com.lgl.scsapi.util.utils.AppUtil;
import com.lgl.scsapi.util.utils.BillNumberTool;
import com.lgl.scsapi.util.utils.Constants;
import com.lgl.scsapi.util.utils.Tools;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * 微信小程序端用户controller
 */
@Slf4j
@RestController
@RequestMapping(value="/wx/index/user")
public class WxUserController extends BaseController {
	@Resource
    private StringRedisTemplate stringRedisTemplate;
	@Resource(name="wxUserService")
	private WxUserService wxUserService;
	@Resource(name="utilService")
	private UtilService utilService;

	//发送短信登录验证码
	@RequestMapping(value="/sendLoginCheckCode")
	public Object sendLoginCheckCode(HttpServletRequest request){
		PageData pd = this.getPageData();
		try {
			String phone = pd.getString("phone");
			if (!Tools.checkMobileNumber(phone)) {
				pd.put("status", "6"); //号码格式不对
				return pd;
			}
			boolean existsPhone = wxUserService.isExistsPhone(phone);
			if(!existsPhone){
				pd.put("status", "9"); //号码不存在
				return pd;
			}
			return AppUtil.checkAndSendCode(request,phone,pd,stringRedisTemplate);
		} catch (Exception e) {
			log.error("送短信登录验证码sendLoginCheckCode报错：",e);
			pd.put("status", "0");
		}
		return pd;
	}
    //发送短信修改手机的证码
    @RequestMapping(value="/sendModifyPhoneCode")
    public Object sendModifyPhoneCode(HttpServletRequest request){
        PageData modifyPd = this.getPageData();
        try {
            AppUser loginUser = getSessionAppUser();
            String phone = modifyPd.getString("phone");
            //后台获取的用户手机号与前台传的手机号不相同，则作未登录处理
            if (loginUser != null && !loginUser.getPhone().equals(phone)){
				modifyPd.clear();
            	modifyPd.put("status", "-2");
                return modifyPd;
            }
            return sendCheckCode(modifyPd,request,loginUser);
        } catch (Exception e) {
            log.error("发送修改手机短信验证码[sendModifyPhoneCode]报错",e);
			modifyPd.clear();
			modifyPd.put("status", "0");
        }
        return modifyPd;
    }
    //发送短信绑定手机的验证码
    @RequestMapping(value="/sendBindPhoneCode")
    public Object sendBindPhoneCode(HttpServletRequest request){
        PageData bpPageData = this.getPageData();
	    try {
	    	String bindPhone = bpPageData.getString("phone");
			boolean isBind = wxUserService.isExistsPhone(bindPhone);
			//如果该手机号已经绑定或注册了
			if (isBind){
				bpPageData.clear();
				bpPageData.put("status","6");
				return bpPageData;
			}
            return sendCheckCode(bpPageData,request,getSessionAppUser());
        } catch (Exception e) {
            log.error("发送绑定手机短信证码[sendBindPhoneCode]报错",e);
            bpPageData.put("status", "0");
        }
        return bpPageData;
    }

    /**
     * 发送短信验证码
     * @param codePd 手机号码等参数
     * @param request HttpServletRequest对象
     * @param loginUser 登录的用户对象
     * @return 成功或失败状态
     */
    private PageData sendCheckCode(PageData codePd,HttpServletRequest request,AppUser loginUser){
        String phone = codePd.getString("phone");
        //如果用户为空，或后台获取的用户手机号与前台传的手机号不相同，则作未登录处理
        if (loginUser == null){
            codePd.put("status", "-1");
            return codePd;
        }
        return AppUtil.checkAndSendCode(request,phone,codePd,stringRedisTemplate);
    }
	//微信小程序密码登录
	@RequestMapping(value="/toLogin")
	public Object toLogin(HttpServletRequest request, HttpServletResponse response){
		setCookie(request,response);
		return ControllerUtil.checkUserLoginResult(this.getPageData(),wxUserService,utilService);
	}
	//微信小程序验证码登录
	@RequestMapping(value="/checkCodeLogin")
	public Object checkCodeLogin(HttpServletRequest request, HttpServletResponse response){
		PageData checkCodePd = this.getPageData();
		String codePhone = checkCodePd.getString("phone"),wxCheckCode = checkCodePd.getString("checkCode");
		//前台传的手机号码为空
		if (StringUtils.isEmpty(codePhone)){
			checkCodePd.clear();
			checkCodePd.put("status","1");
			return checkCodePd;
		}
		//前台传的验证码为空
		if (StringUtils.isEmpty(wxCheckCode)){
			checkCodePd.clear();
			checkCodePd.put("status","2");
			return checkCodePd;
		}
		String phoneKey = Constants.PHONE_CHECK_CODE_K_PRE + codePhone;
		//从redis中取验证码，并与传过来的比较
		String redisCheckCode = stringRedisTemplate.opsForValue().get(phoneKey);
		if (!StringUtils.hasText(redisCheckCode) || !wxCheckCode.equals(redisCheckCode)) {
			checkCodePd.clear();
			checkCodePd.put("status", "9");
			return checkCodePd;
		}
		AppUser appUser = wxUserService.findByPhone(codePhone);
		String userPwd = appUser == null ? null : appUser.getPassword();
		setCookie(request,response);
		if (appUser != null){
			appUser.setPassword(userPwd);
		}
		return ControllerUtil.checkAndLoginUser(appUser,checkCodePd,utilService);
	}
	//微信用户openid注册并登录
	@RequestMapping(value="/wxLogin",method = RequestMethod.GET)
	public Object wxLogin(HttpServletRequest request, HttpServletResponse response) {
		String jsCode = request.getParameter("jsCode");
		String openIdSessionKey = wxUserService.getWxSessionKeyAndOpenId(jsCode);
		JSONObject openJsonObject = JSON.parseObject(openIdSessionKey);
		String openId = openJsonObject.getString("openid");
		log.info("-------openid=" + openId);
		PageData openIdPd = new PageData(4);
		//如果openid为空，说明获取失败
		if (!StringUtils.hasText(openId)){
			//登录失败，有errcode和errorMsg属性值，没有openid和session_key这2个属性值
			openIdPd.put("code",openJsonObject.getString("errcode"));
			log.error(openIdSessionKey);
			return openIdPd;
		}
		openIdPd.put("openId",openId);
		AppUser wxUser = wxUserService.findByOpenId(openIdPd);
		//如果openid不存在，则保存到数据里
		if (wxUser == null){
			wxUser = initAppUser(openIdPd);
		}
		//已冻结
		if ("1".equals(wxUser.getStatus())){
			openIdPd.put("code","5");
			return openIdPd;
		}
		Subject subject = SecurityUtils.getSubject();
		UsernamePasswordToken token = new UsernamePasswordToken(wxUser.getPhone(), wxUser.getPassword());
		subject.login(token);
		subject.getSession().setAttribute(Constants.SESSION_USER, wxUser);
		openIdPd.clear();
		//登录成功有openid和session_key这2个属性值，但这2个值不可以传到前台
		openIdPd.put("code","0");
		openIdPd.put("userId",wxUser.getUserId());
		openIdPd.put("userName",wxUser.getUserName());
		openIdPd.put("userPhone",wxUser.getPhone());
		setCookie(request,response);
		return openIdPd;
	}

	/**
	 * 初始化微信注册或登录的用户，并保存到数据库
	 * @param wxUserPd 需要初始化用户的其它数据
	 * @return 返回初始化后的用户
	 */
	private AppUser initAppUser(PageData wxUserPd){
		String initValue = Constants.USER_DEFAULT_PASSWORD;
		String userId = BillNumberTool.getRegisterUserId(stringRedisTemplate,0);
		AppUser wxUser = new AppUser();
		//将用户名、手机号初始化为用户ID
		wxUser.setUserId(userId).setUserName(userId).setPhone(userId)
              .setPassword(initValue).setOpenId(wxUserPd.getString("openId"));
		wxUserService.saveWxUser(wxUser);
		//如果userId为空，则再重新获取一次userId
		if(!StringUtils.hasText(userId)){
		    AppUser user = wxUserService.findByOpenId(wxUserPd);
		    wxUser.setUserId(user.getUserId());
        }
		return wxUser;
	}
	//通过验证码注册
	@RequestMapping("/registerByCheckCode")
	public Object registerByCheckCode(HttpServletRequest request, HttpServletResponse response) {
		PageData pd = this.getPageData();
		try {
			pd = wxUserService.registerByCheckCode(pd);
			//如果注册失败
			if (!"S".equals(pd.getString("status"))){
				return pd;
			}
			AppUser registerUser = (AppUser) pd.remove("appUser");
			setCookie(request,response);
			return ControllerUtil.checkAndLoginUser(registerUser,pd,utilService);
		} catch (Exception e) {
            log.error("通过验证码注册时报错了：registerByCheckCodePd:" + pd,e);
            pd.clear();
			pd.put("status", "E");
		}
		return pd;
	}
	//通过微信注册
	@RequestMapping("/registerByWeChat")
	public Object registerByWeChat(HttpServletRequest request, HttpServletResponse response) {
		PageData pd = this.getPageData();
		AppUser wxUser = null;
		String jsCode = (String) pd.remove("jsCode");
		//如果未获取到微信用户登录凭证
		if (StringUtils.isEmpty(jsCode)){
			pd.put("status","1");
			return pd;
		}
		String openId = wxUserService.getWxOpenId(jsCode);
		pd.clear();
		pd.put("openId",openId);
		wxUser = wxUserService.findByOpenId(pd);
		//如果不为空，则说明已注册
		if (wxUser != null){
			pd.remove("openId");
			pd.put("status","2");
			return pd;
		}
		//如果出现主键重复，则重试3次，如果3次后还有问题，则直接返回错误提示，否则返回成功
		for (int i = 0; i < 3; i ++){
			try {
				wxUser = initAppUser(pd);
				setCookie(request,response);
				return ControllerUtil.checkAndLoginUser(wxUser,pd,utilService);
			} catch (Exception e) {
				String errStr = e.toString();
				boolean isDuplicateKey = e instanceof DuplicateKeyException && errStr.contains("PRIMARY");
				//如果不是主键重新
				if (!isDuplicateKey){
					pd.clear();
					pd.put("status", "E");
					return pd;
				}
				log.error("通过微信注册时报错了，重试第[" + (i + 1) + "]次，registerByWeChat:" + pd + ",\n wxUser=" +
						(wxUser == null ? "空" : wxUser.toString()),e);
			}
		}
		pd.clear();
		pd.put("status", "E");
		return pd;
	}
	/**
	 * 设置cookie
	 * @param request HttpServletRequest对象
	 * @param response HttpServletResponse对象
	 */
	private void setCookie(HttpServletRequest request, HttpServletResponse response){
		String sessionId = request.getSession().getId();
		response.setHeader("JSESSIONID","JSESSIONID=" + sessionId + ";path=/lgl,HttpOnly");
	}

	/**
	 * 判断该手机号是否绑定了微信
	 * @param request HttpServletRequest对象
	 * @return N:未绑定，Y：已绑定
	 */
	@RequestMapping("/phoneIsBindWx")
	public Object phoneIsBindWx(HttpServletRequest request){
		String phone = request.getParameter("phone");
		if (StringUtils.isEmpty(phone)){
			return "N";
		}
		PageData pageData = new PageData(1);
		pageData.put("phone",phone);
		AppUser wxUser = wxUserService.findByPhone(phone);
		//如果wxUser为空，或wxUser的openid为空，则说明未绑定
		return (wxUser == null || StringUtils.isEmpty(wxUser.getOpenId())) ? "N" : "Y";
	}
	//通过用户openid和手机号登录
	@RequestMapping(value="/loginByWxAndPhone",method = RequestMethod.GET)
	public Object loginByWxAndPhone(HttpServletRequest request, HttpServletResponse response) {
		String jsCode = request.getParameter("jsCode"),phone = request.getParameter("phone");
		String openId = wxUserService.getWxOpenId(jsCode);
		log.info("-------loginByWsAndPhone.openid=" + openId);
		PageData openIdPd = new PageData(4);
		//如果openid为空，说明获取失败
		if (!StringUtils.hasText(openId)){
			//登录失败
			openIdPd.put("status","1");
			return openIdPd;
		}
		openIdPd.put("openId",openId);
		openIdPd.put("phone",phone);
		AppUser wxUser = wxUserService.findByOpenId(openIdPd);
		//如果wxUser为空，则说明手机号与微信未绑定
		if (wxUser == null){
			openIdPd.put("status","2");
			return openIdPd;
		}
		setCookie(request,response);
		openIdPd.clear();
		return ControllerUtil.checkAndLoginUser(wxUser,openIdPd,utilService);
	}
    //查询我自己的其它信息
    @RequestMapping("/seeMyOtherInfo")
    public Object seeMyOtherInfo() {
        PageData pd = this.getPageData();
        try {
            AppUser appUser = this.getSessionAppUser();
            if (appUser == null || !SecurityUtils.getSubject().isAuthenticated()) {
                pd.put("status", "-1");
                return pd;
            }
            pd = wxUserService.getMyOtherInfoByName(pd,appUser);
        } catch (Exception e) {
            log.error(e.toString(),e);
            pd.put("status", "1");
            return pd;
        }
        Map<String, Object> otherInfoMap = new HashMap<String, Object>(2);
        otherInfoMap.put("uoi", pd);
        otherInfoMap.put("status", "0");
        return otherInfoMap;
    }
	//修改用户信息
	@RequestMapping("/editUserInfo")
	public Object editUserInfo() {
		PageData pd = this.getPageData();
		AppUser appUser = this.getSessionAppUser();
		if (appUser == null){
			pd.clear();
			pd.put("status", "-1");
			return pd;
		}
		String editResult = "0";
		try {
			wxUserService.editMyOtherInfo(pd,appUser);
		} catch (Exception e) {
			log.error(e.toString(),e);
			editResult = "1";
		}
		pd.clear();
		return editResult;
	}
    //修改手机号码
    @RequestMapping("/modifyMyPhone")
    public Object modifyMyPhone() {
        PageData pd = this.getPageData();
        try {
            AppUser appUser = this.getSessionAppUser();
			if (appUser == null) {
				pd.put("status", "-1");
				return pd;
			}
            String modifyStatus = wxUserService.modifyMyPhone(appUser,pd);
            pd.clear();
            return modifyStatus;
        } catch (Exception e) {
            log.error("微信端修改手机号码失败[modifyMyPhone]",e);
            return "1";
        }
    }
	//查询我的手机
	@RequestMapping("/getMyPhone")
	public Object getMyPhone() {
		PageData mpPageData = new PageData(2);
		try {
			AppUser appUser = this.getSessionAppUser();
			if (appUser == null) {
				mpPageData.put("status", "-1");
				return mpPageData;
			}
			mpPageData = wxUserService.getMyOtherInfoByName(mpPageData,appUser);
		} catch (Exception e) {
			log.error(e.toString(),e);
			mpPageData.put("status", "1");
			return mpPageData;
		}
		String myPhone = mpPageData.getString("phone");
		//不返回多余的数据给前端，以免占用更多的网络带宽
		mpPageData.clear();
		mpPageData.put("myPhone", myPhone);
		mpPageData.put("status", "0");
		return mpPageData;
	}
    //绑定我的手机号码
    @RequestMapping("/bindMyPhone")
    public Object bindMyPhone() {
        PageData bindPageData = this.getPageData();
        try {
            AppUser appUser = this.getSessionAppUser();
			bindPageData = wxUserService.bindMyPhone(appUser,bindPageData);
            return bindPageData;
        } catch (Exception e) {
            log.error("微信端修改手机号码失败[modifyMyPhone]",e);
			bindPageData.clear();
			bindPageData.put("status","1");
        }
		return bindPageData;
    }
    //查询我的密码设置情况,0:设置了密码，1：未设置
    @RequestMapping("/pwdSetStatus")
    public Object pwdSetStatus() {
        AppUser appUser = this.getSessionAppUser();
        PageData pd = new PageData(1);
        int isSetPwd = 0;
        if (appUser == null) {
            pd.put("status", "-1");
            return pd;
        }
        String pwd = appUser.getPassword();
        if (!StringUtils.hasText(pwd) || pwd.length() < 10){
        	pd.put("phone",appUser.getPhone());
			appUser = wxUserService.findByPhone(appUser.getPhone());
			pwd = appUser.getPassword();
			if (!StringUtils.hasText(pwd) || pwd.length() < 10){
				isSetPwd = 1;
				pd.clear();
			}
        }
        pd.put("isSetPwd",isSetPwd);
        return pd;
    }
    //通过验证码修改密码
    @RequestMapping("/resetPwdByCheckCode")
    public Object resetPwdByCheckCode() {
        PageData pd = this.getPageData();
        try {
			return wxUserService.resetPwdByCheckCode(pd);
        } catch (Exception e) {
            log.error(e.toString(),e);
            pd.put("status", "1");
        }
        return pd;
    }
	//登录后通过验证码修改密码
	@RequestMapping("/resetPwdByLoginCode")
	public Object resetPwdByLoginCode() {
		PageData pd = this.getPageData();
		try {
			AppUser loginUser = getSessionAppUser();
			if (loginUser == null){
				pd.clear();
				pd.put("status", "-1");
				return pd;
			}
			pd.put("phone",loginUser.getUserName());
			return wxUserService.resetPwdByCheckCode(pd);
		} catch (Exception e) {
			log.error(e.toString(),e);
			pd.put("status", "1");
		}
		return pd;
	}
	//通过原密码修改密码
	@RequestMapping("/resetPwdByOldPwd")
	public Object resetPwdByOldPwd() {
		PageData pd = this.getPageData();
		try {
			AppUser loginUser = getSessionAppUser();
			pd = wxUserService.resetPwdByOldPwd(loginUser,pd);
		} catch (Exception e) {
			log.error(e.toString(),e);
			pd.put("status", "1");
		}
		return pd;
	}
	//用户退出
    @RequestMapping(value="/toLogout")
    public String toLogout(){
        //shiro管理的session
        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession();
        session.removeAttribute(Constants.SESSION_USER);
        //shiro销毁登录
        subject.logout();
        return "0";
    }
}